/*
						  qpdf

	Copyright (C) 2015 Arthur Benilov,
	arthur.benilov@gmail.com
	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.
	This software is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
	Lesser General Public License for more details.
*/

#include <QApplication>
#include <QWebChannel>
#include <QWebEngineSettings>
#include <QContextMenuEvent>
#include <QSemaphore>
#include "PdfJSBridge.h"
#include <QJsonArray>
#include "BridgeObject.h"

PdfJsBridge::PdfJsBridge(QWidget* pParent)
	: QWebEngineView(pParent)
	, m_nCurrentPage(0)
	, m_nPageCount(0)
	, m_fPageScale(0)
{
	m_pWebChannel = new QWebChannel(this);
	m_pBridgeObject = new BridgeObject(this);

	connect(this, &QWebEngineView::loadFinished, this, &PdfJsBridge::onLoadFinished);
}

PdfJsBridge::~PdfJsBridge()
{
	delete m_pWebChannel;
}

void PdfJsBridge::invokeJavaScript(const QString& script)
{
	QWebEnginePage* pPage = page();
	if (pPage != nullptr)
	{
		pPage->runJavaScript(script);
	}
}

QVariant PdfJsBridge::invokeJavaScriptAndWaitForResult(const QString& script)
{
	QVariant result;
	QWebEnginePage* pPage = page();
	if (pPage != nullptr)
	{
		QSemaphore waitSemaphore;
		pPage->runJavaScript(script, [&result, &waitSemaphore](const QVariant& res) {
			result = res;
			waitSemaphore.release();
		});

		while (!waitSemaphore.tryAcquire())
		{
			qApp->processEvents();
		}
	}

	return result;
}

QStringList PdfJsBridge::fetchPdfDocumentDestinations()
{
	m_pdfDocumentDestinations.clear();

	invokeJavaScript("qpdf_FetchDestinations();");

	while (!m_pdfDestinationsSema.tryAcquire())
	{
		qApp->processEvents();
	}

	return m_pdfDocumentDestinations;
}

void PdfJsBridge::setToolbarVisible(bool visible)
{
	invokeJavaScript(QString("qpdf_ShowToolbar(%1)").arg(visible ? "true" : "false"));
}

QList<QJsonObject> PdfJsBridge::getChapterList()
{
	m_pdfChapterList.clear();
	invokeJavaScript("qpdf_ChapterList();");
	while (!m_pdfDestinationsSema.tryAcquire())
	{
		qApp->processEvents();
	}
	return m_pdfChapterList;
}

void PdfJsBridge::gotoChapter(const QString& strHref)
{
	invokeJavaScript(QString("qpdf_GoToChapter('%1');").arg(strHref));
}

int PdfJsBridge::GetCurrentPage()
{
	m_pdfChapterList.clear();
	invokeJavaScript("qpdf_GetCurrentPage();");
	while (!m_pdfDestinationsSema.tryAcquire())
	{
		qApp->processEvents();
	}
	return m_nCurrentPage;
}

void PdfJsBridge::close()
{
	invokeJavaScript("qpdf_Close();");
	while (!m_pdfCloseSema.tryAcquire())
	{
		qApp->processEvents();
	}
}

void PdfJsBridge::jsInitialized()
{
	// Web view has been initialized
}

void PdfJsBridge::jsReportDestinations(const QStringList& destinations)
{
	m_pdfDocumentDestinations = destinations;
	m_pdfDestinationsSema.release();
}

void PdfJsBridge::jsChapterList(const QJsonValue& list)
{
	m_pdfChapterList.clear();
	QJsonArray arr = list.toArray();
	for (int i = 0; i < arr.count(); ++i)
	{
		QJsonObject jsonObj;
		jsonObj.insert("parentText", arr[i].toObject()["parentText"].toString());
		jsonObj.insert("text", arr[i].toObject()["text"].toString());
		jsonObj.insert("href", arr[i].toObject()["text"].toString());
		m_pdfChapterList.push_back(jsonObj);
	}
	m_pdfDestinationsSema.release();
}

void PdfJsBridge::jsChapterListLoaded(const QJsonValue& list)
{
	m_pdfChapterList.clear();
	QJsonArray arr = list.toArray();
	for (int i = 0; i < arr.count(); ++i)
	{
		QJsonObject jsonObj;
		jsonObj.insert("parentText", arr[i].toObject()["parentText"].toString());
		jsonObj.insert("text", arr[i].toObject()["text"].toString());
		jsonObj.insert("href", arr[i].toObject()["href"].toString());
		m_pdfChapterList.push_back(jsonObj);
	}
	emit chapterListLoaded(m_pdfChapterList);
}

void PdfJsBridge::jsCurrentPage(int nPage)
{
	m_nCurrentPage = nPage;
	emit pageNumberChanged(m_nCurrentPage);
}

void PdfJsBridge::jsPageCount(int nPageCount)
{
	m_nPageCount = nPageCount;
	emit pageCountChanged(m_nPageCount);
}

void PdfJsBridge::jsPageScale(double fScale)
{
	m_fPageScale = fScale;
	emit pageScaleChanged(fScale);
}

void PdfJsBridge::jsLoaded()
{
	emit pdfDocumentloaded();
}

void PdfJsBridge::jsClosed()
{
	m_pdfCloseSema.release();
}

void PdfJsBridge::jsFindState(int nState)
{
	emit findStateChanged(nState);
}

void PdfJsBridge::contextMenuEvent(QContextMenuEvent* pEvent)
{
	// Disable context menu completely
	pEvent->ignore();
}

void PdfJsBridge::onLoadFinished(bool ok)
{
	if (ok)
	{
		establishWebChannel();
		emit initialized();
	}
}

void PdfJsBridge::establishWebChannel()
{
	QWebEnginePage* pPage = page();
	if (pPage != nullptr)
	{
		pPage->setWebChannel(m_pWebChannel);
		m_pWebChannel->registerObject(QStringLiteral("qpdfbridge"), m_pBridgeObject);

		pPage->runJavaScript("qpdf_Initialize();");
	}
}